<?php
declare(strict_types=1);

namespace WebApp\lib\app;

use ErrorException;
use Exception;
use Throwable;
use WebApp\lib\Controller;
use WebApp\lib\util\Util;

/**
 * Class AbstractApp
 * @package WebApp\lib\app
 */
Abstract class AbstractApp
{
    protected $module = "";

    protected $controller = '';
    protected $action = '';
    /**
     * @var ReqProxy
     */
    public $request;
    /**
     * @var RespProxy
     */
    public $response;

    public $_SESSION = [];
    protected $isSessionListenChange = false;
    protected $isSessionChange = false;

    /**
     * AbstractApp constructor.
     * @param $request
     * @param $response
     */
    public function __construct(&$request, &$response)
    {
        $this->request = new ReqProxy($request);
        $this->response = new RespProxy($response);
    }

    public function __destruct()
    {
        unset($this->request);
        unset($this->response);
        $this->sessionEnd();
    }

    final public function run(): void
    {
        tracker(D, 'get', $this->getRequest()->get());
        tracker(D, 'post', $this->getRequest()->post());

        $class_method = $this->getRoute();
        $this->setController($class_method[0]);
        $this->setAction($class_method[1]);
        try {
            /** @var Controller $obj */
            $str = 'return (new ' . $this->module . '\controller\\' . $this->getController() . 'Controller($this));';
            $obj = eval($str);
            $str = eval('return $obj->action' . $this->getAction() . '();');
            if ($str === null) $str = "";
            $this->response->write(is_string($str) ? $str:json_encode(array_merge([
                'err' => 0,
            ], $str)));
        } catch (Throwable $e) {
            $code = $e->getCode();
            $msg = $e->getMessage();
            if ($code !== 111 || $msg !== "111") {
                tracker(E, $e->getMessage(), $e->getFile(), $e->getLine());
                tracker(E, $e->getTraceAsString());

                $class_name = get_class($e);
                $class_name_arr = explode('\\', $class_name);
                $type = end($class_name_arr);
                if ($type !== "ClientErrException") {
                    $msg = "";
                }
                $this->response->write(json_encode([
                    'err' => -1,
                    'type' => $type,
                    'code' => $code,
                    'msg' => $msg,
                ]));
            }
        }
        unset($obj);
    }

    /**
     * @return array
     */
    private function getRoute(): array
    {
        try {
            if (!$route = $this->getRequest()->hasGet("r"))
                throw new ErrorException();
            $routeArr = explode('/', $route);
            if (!$routeArr[0] && !$routeArr[1])
                throw new ErrorException();
            return [
                ucfirst(strtolower($routeArr[0])),
                ucfirst(strtolower($routeArr[1]))
            ];
        } catch (ErrorException $e) {
            return ['Index', 'Login'];
        }
    }

    /**
     * 页面重定向
     * @param $route
     * @param $getArr
     * @throws Exception
     */
    public function redirect($route, $getArr = [])
    {
        if (Util::startWitsh($route, 'http')) {
            $url = $route;
        } else {
            $url = $this->getRequest()->header()["path_info"] . "?r=" . $route;
        }
        if (count($getArr) >= 0) {
            foreach ($getArr as $k => $v) {
                $url .= "&{$k}={$v}";
            }
        }
        //location
//        header('Location:'.$url);
        $this->getResponse()->end(<<<TAG
<script>location.href='{$url}';</script>
TAG
        );
    }

    /**
     * 渲染html页面
     * @param $fileName
     * @param $paramsArr
     * @throws Exception
     */
    public function render($fileName, $paramsArr = null)
    {
        if ($paramsArr !== null) {
            extract($paramsArr);
        }
        ob_start();
        include(ROOT_DIR . 'app/view/' . $fileName . '.php' . '');
        $res = ob_get_contents();
        ob_clean();
        $this->getResponse()->end($res);
    }

    /**
     * @param $fileName
     * @param null $paramsArr
     * @throws Exception
     */
    public function pullPage($fileName, $paramsArr = null)
    {
        if ($paramsArr !== null) {
            extract($paramsArr);
        }
        ob_start();
        include($fileName . '');
        $res = ob_get_contents();
        ob_clean();
        $this->getResponse()->end($res);
    }

    /**
     * @param $key
     * @param $val
     * @internal param array $SESSION
     */
    public function setSESSION($key, $val)
    {
        if ($this->isIsSessionListenChange()) {
            $this->setIsSessionChange(true);
        }

        $this->_SESSION[$key] = $val;
    }

    /**
     * @return boolean
     */
    public function isIsSessionListenChange()
    {
        return $this->isSessionListenChange;
    }

    /**
     * @param boolean $isSessionListenChange
     */
    public function setIsSessionListenChange($isSessionListenChange)
    {
        $this->isSessionListenChange = $isSessionListenChange;
    }

    /**
     * @return boolean
     */
    public function isIsSessionChange()
    {
        return $this->isSessionChange;
    }

    /**
     * @param boolean $isSessionChange
     */
    public function setIsSessionChange($isSessionChange)
    {
        $this->isSessionChange = $isSessionChange;
    }

    /**
     * @return string
     */
    public function getAction()
    {
        return $this->action;
    }

    /**
     * @param string $action
     */
    public function setAction($action)
    {
        $this->action = $action;
    }

    /**
     * @return string
     */
    public function getController()
    {
        return $this->controller;
    }

    /**
     * @param string $controller
     */
    public function setController($controller)
    {
        $this->controller = $controller;
    }

    abstract public function sessionEnd(): void;

    /**
     * @return RespProxy
     */
    public function getResponse()
    {
        return $this->response;
    }

    /**
     * @return ReqProxy
     */
    public function getRequest()
    {
        return $this->request;
    }
}
